#include "ServiceInfomation.h"
#include "ServiceConnection.h"
#include "ServiceManager.h"
#include "App.h"
#include "Global.h"
#include "MessageHelper.h"
#include "json.hpp"
#include "AdbHelper.h"

#include <thread>
#include <giomm/socket.h>
#include <glibmm/main.h>
#include <giomm/inetsocketaddress.h>
#include <string>
#include <cstring>

namespace Gnodroid {

    ServiceInfomation::ServiceInfomation() :
        Service(SERVICE_INFOMATION)
    {}

    void
    ServiceInfomation::start()
    {}

    void
    ServiceInfomation::stop()
    {}

    void
    ServiceInfomation::getDeviceProp(sigc::slot<void, bool, std::shared_ptr<DeviceProp> > slotCb)
    {
        ServiceConnection* serviceConnection = static_cast<ServiceConnection*>(App::getCurrentApp()
                                        ->getServiceManager()
                                        ->getService(SERVICE_CONNECTION));

        if (!serviceConnection->isConnecting()) {
            slotCb(false, nullptr);
            LOG_ERROR("device not connected");
            return;
        }

        std::thread t (sigc::mem_fun(*this, &ServiceInfomation::__getDevicePropThread), slotCb);
        t.detach();
    }

    void
    ServiceInfomation::__getDevicePropThread(sigc::slot<void, bool, std::shared_ptr<DeviceProp> > slotCb)
    {
        ServiceConnection* serviceConnection = static_cast<ServiceConnection*>(App::getCurrentApp()
                                        ->getServiceManager()
                                        ->getService(SERVICE_CONNECTION));
        auto mainContext = App::getCurrentApp()->getMainContext();

        auto socket = Gio::Socket::create(Gio::SOCKET_FAMILY_IPV4,
                                          Gio::SOCKET_TYPE_STREAM,
                                          Gio::SOCKET_PROTOCOL_TCP);
        socket->set_timeout(5);

        try {
            socket->connect(Gio::InetSocketAddress::create(
                                Gio::InetAddress::create("127.0.0.1"),
                                        serviceConnection->getPcPort()));
        } catch (...) {
            mainContext->invoke([&slotCb]()->bool{slotCb(false, nullptr); return false;});
            return;
        }

        char* mes = R"({"service":"Infomation","method":"GetProp"})";

        if (!MessageHelper::sendMessage(mes, std::strlen(mes) + 1, socket)) {
            mainContext->invoke([&slotCb]()->bool{slotCb(false, nullptr); return false;});
            socket->close();
            return;
        }

        char* recMes = nullptr;
        gssize recLen;

        if (!MessageHelper::receiveMessage(recMes, recLen, socket)) {
            mainContext->invoke([&slotCb]()->bool{slotCb(false, nullptr); return false;});
            socket->close();
            return;
        }

        nlohmann::json recJson;
        try {
             recJson = nlohmann::json::parse(recMes);
        } catch (...) {
            mainContext->invoke([&slotCb]()->bool{slotCb(false, nullptr); return false;});
            socket->close();
            delete [] recMes;
            return;
        }

        //delete unused buffer
        if (recMes) {
            delete [] recMes;
            recMes = nullptr;
        }

        std::shared_ptr<DeviceProp> deviceProp(new DeviceProp());

        deviceProp->model = recJson["model"];
        deviceProp->brand = recJson["brand"];
        deviceProp->sdk = recJson["sdk"];
        deviceProp->android_ver = recJson["android_ver"];
        deviceProp->kernel = recJson["kernel"];
        deviceProp->sc_width = recJson["sc_width"];
        deviceProp->sc_height = recJson["sc_height"];
        deviceProp->ram = recJson["ram"];
        deviceProp->rom = recJson["rom"];

        mainContext->invoke([deviceProp, slotCb]()->bool{
            slotCb(true, deviceProp);
            return false;
        });

        return;
    }

    void
    ServiceInfomation::getScreenCap(std::shared_ptr<bool> isContinue,
                                    std::shared_ptr<int> interval,
                                    sigc::slot<void, bool, Glib::RefPtr<Gio::MemoryInputStream> > slotCb)
    {
        ServiceConnection* serviceConnection = static_cast<ServiceConnection*>(App::getCurrentApp()
                                        ->getServiceManager()
                                        ->getService(SERVICE_CONNECTION));
        if (!serviceConnection->isConnecting()) {
            LOG_ERROR("not connect");
            slotCb(false, Glib::RefPtr<Gio::MemoryInputStream>());
            return;
        }

        std::thread t (sigc::mem_fun(*this, &ServiceInfomation::__getScreenCapThread),
                                    isContinue, interval, slotCb);
        t.detach();
    }

    void
    ServiceInfomation::__getScreenCapThread(std::shared_ptr<bool> isContinue,
                                            std::shared_ptr<int> interval,
                                            sigc::slot<void, bool, Glib::RefPtr<Gio::MemoryInputStream> > slotCb)
    {
        ServiceConnection* serviceConnection = static_cast<ServiceConnection*>(App::getCurrentApp()
                                        ->getServiceManager()
                                        ->getService(SERVICE_CONNECTION));
        auto context = App::getCurrentApp()->getMainContext();
        //using namespace std::chrono_literals;


        while (true) {
            //std::string data;
            auto memInputStream = Gio::MemoryInputStream::create();
            if (!*isContinue
                    || !serviceConnection->isConnecting()
                    || !AdbHelper::screenCap(serviceConnection->getAdbPath(),
                                             serviceConnection->getDeviceId(),
                                             memInputStream)) {
                context->invoke([slotCb]()->bool{slotCb(false, Glib::RefPtr<Gio::MemoryInputStream>());return false;});
                LOG("exit screencap thread");
                return;
            }

            //auto memInputStream = Gio::MemoryInputStream::create();
            //LOG();
            //LOG("len: " + std::to_string(data.length()));
            //memInputStream->add_data(data);
            memInputStream->seek(0, Glib::SEEK_TYPE_END);
            int len = memInputStream->tell();
            memInputStream->seek(0, Glib::SEEK_TYPE_SET);
            LOG("len: " + std::to_string(len));
            context->invoke([slotCb, memInputStream]()->bool{
                slotCb(true, memInputStream);
                return false;
            });
            if (!*isContinue) {
                context->invoke([slotCb]()->bool{slotCb(false, Glib::RefPtr<Gio::MemoryInputStream>());return false;});
                LOG("exit screencap thread");
                return;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(*interval));
        }
    }

}
